var Playlist = new Class({
	initialize: function () {
		this.$songlist = [];
		this.$cur = -1;
	},
	
	sort: function(sortRoutine) {
		var currentSong = this.currentSong;
		this.$songlist.sort(sortRoutine);
		this.$cur = this.$songlist.find(function (item, i) {
			for (var prop in item) {
				if ($type(item[prop]) != 'function') {
					if (item[prop] != currentSong[prop]) {
						return false;
					}
				}
			}
			return true;
		});
		
		this.fireEvent('onPlaylistSorted', 0);
	},
	
	get size () {
		return this.$songlist.length;
	},
	
	get: function(index) {
		if (index.inRange(0, this.$songlist.length)) {
			return this.$songlist[index];
		}
		return null;
	},
	
	get currentIndex () {
		return this.$cur;
	},
	
	set currentIndex (index) {
		if (this.$songlist.length > 0) {
			if (index.inRange(0, this.$songlist.length)) {
				var oldCur = this.$cur;
				this.$cur = index;
				this.fireEvent('onCurrentSongChanged', [this.$cur, oldCur], 0);
			} else if (index >= this.$songlist.length) {
				this.fireEvent('onPlaylistFinished', 0);
			}
		}
	},
	
	reset: function() {
		this.currentIndex = 0;
	},
	
	prev: function() {
		this.currentIndex = this.$cur - 1;
	},
	
	next: function() {
		this.currentIndex = this.$cur + 1;
	},
	
	get currentSong () {
		if (this.$songlist.length > 0 && this.$cur.inRange(0, this.$songlist.length)) {
			return this.$songlist[this.$cur];
		} else {
			return null;
		}
	},
	
	addSongs: function(songs) {	
		if ($type(songs) == 'array' 
			&& songs.length != 0) {
			this.$songlist.extend(songs);
			this.fireEvent('onAddSong', [songs], 0);
			
			if (!this.currentSong) {
				this.reset();
			}
		}
	},
	
	addSong: function (song, index) {
		this.addSongs([song]);	
	},
	
	removeSong: function (args) {
		if ($type(args) != 'array') {
			args = [args];
		}
		
		args = args.filter(function (item) {
			return item.inRange(0, this.$songlist.length);
		}.bind(this))
		.unique()
		.sort(function (a, b){
			return b - a; 
		});
		
		var curUpdated = false;
		args.each(function (index) {
			this.$songlist.splice(index, 1);
			if (index < this.$cur) {
				this.$cur--;
				curUpdated = true;
			} else if (index == this.$cur) {
				this.$cur = index.limit(0, this.$songlist.length - 1);
				curUpdated = true;
			}
		}.bind(this));
		
		if (curUpdated) {
			this.currentIndex = this.$cur;
		}
		
		if (args.length != 0) {
			this.fireEvent('onRemoveSong', [args], 0);
		}
				
		if (this.$songlist.length == 0) {
			this.$cur = -1;
			this.fireEvent('onPlaylistEmpty', 0);
		}
	},
	
	empty: function () {
		this.$songlist = [];
		this.$cur = -1;
		var removed = [];
		for (i = 0; i < this.$songlist.length; i++) {
			removed.push(i);
		}
		this.fireEvent('onRemoveSong', [removed], 0);
		this.fireEvent('onPlaylistEmpty', 0);
	}
	
});
Playlist.implement(new Events);

var SongInfo = new Class({
	options: {
		selectionStyle: {},
		currentStyle : {},
		defaultStyle: {
			evenBackgroundColor: "transparent",
			oddBackgroundColor: "transparent"
		},
		hOffset: 10,
		height: 15
	},
	
	initialize: function(playlist, frame, options) {
		this.setOptions(options);
		// instance vars
		this.$selected = false;
		this.$current  = false;
		this.$index = -1;
		this.$playlist = playlist;
		
		this.$row = new Element('frame', {
			'width': frame.width - this.options.hOffset,
			'hOffset': this.options.hOffset,
			'styles': this.options.defaultStyle,
			'events': {
				'MultiClick': function (event) {
					this.$playlist.currentIndex = this.index;
				}.bind(this)
			}
		})
		
		this.$songText = new Element('text', {
			'styles': {
				'text-align': 'left'
			},
			'width':  this.$row.width,
			'anchorStyle': "topLeft",
			'vOffset': 0
		});
		this.$songLength = new Element('text', {
			'styles': {
				'text-align': 'right'
			},
			'width':  this.$row.width,
			'anchorStyle': "topLeft",
			'vOffset': 0
		});
		
		this.$row.adopt(this.$songText, this.$songLength);
		frame.appendChild(this.$row);
		
		this.$parentStyle = KonStyle.cloneStyle(this.$row);
	},
		
	get index () {
		return this.$index;
	},
	
	set index (index) {
		this.$index = index;
		
		var song = this.$playlist.get(index);
		if (song) {		
			this.$songText.set({
				'text': song.track.fixedString(2) + " " + song.toString()
			});
			this.$songLength.set({
				'text': song.length.toString()
			});
			this.$row.set({
				'id': "song" + index,
				'vOffset': index * this.options.height - this.$row.getParent().vScroll.value
			});
			if (this.$index % 2 != 0) {
				this.$row.style.backgroundColor = this.options.defaultStyle.evenBackgroundColor
			}
			else {
				this.$row.style.backgroundColor = this.options.defaultStyle.oddBackgroundColor;
			}
			
			this.selected = this.$selected;
			this.current = this.$current;
		}
	},
	
	get element () {
		return this.$row;
	},
	
	// internal
	$updateStyle: function (prop, newvalue, style) {
		if (this[prop] != newvalue) {
			this.$row.toggleStyle(prop, this.$parentStyle, style);
			this[prop] = newvalue;
		}
	},
	
	get selected () {
		return this.$selected;
	},
	
	set selected (selected) {
		this.$updateStyle('$selected', selected, this.options.selectionStyle);
	},
	
	get current () {
		return this.$current;
	},
	
	set current (current) {
		this.$updateStyle('$current', current, this.options.currentStyle);
	},
	
	set width (width) {
		if (this.$row.width != width) {
			this.$row.width = width - this.options.hOffset;
			this.$songText.width = this.$row.width;
			this.$songLength.width = this.$row.width;
		}
	},
	
	set visible (visible) {
		this.$row.visible = visible;
	},
	
	get visible () {
		return this.$row.visible;
	},
	
	addEvent: function (type, fn) {
		this.$row.addEvent(type, fn);
	},
	
	removeEvent: function (type, fn) {
		this.$row.removeEvent(type, fn);
	},
	
	remove: function () {
		this.$row.removeFromParentNode();
	}
});
SongInfo.implement(new Options);

var PlaylistUI = new Class({
	options: {
		selectionStyle: {
			backgroundColor: "rgb(128,128,128)"
		},
		
		currentStyle : {
			fontStyle: "italic",
			fontWeight: "bold"
		},
		
		defaultStyle: {
			fontFamily: "'Arial'",
			color: "white",
			oddBackgroundColor: "rgba(20, 20, 20, 0.5)",
			evenBackgroundColor: "rgba(40, 40, 40, 0.5)"
		}
	},

	initialize: function (frame, playlist, options) {
		this.setOptions(options);
		var songInfos = [];
		var sel = [];
		var last = 0;
		var clicked = 0;
		var height = 15;
		var scroll = frame.vScroll;
		frame.empty();
		
		var setScrollMax = function () {
			var max = playlist.size * height - scroll.pageSize;
			scroll.max = max < 0 ? 0 : max;
			if (!frame.visible) {
				scroll.visible = false;
			}
		};
		
		scroll.pageSize = frame.height;
		scroll.min = 0;
		setScrollMax();
		
		var parentWindow = $(frame.evaluate('ancestor::window').item(0));
		parentWindow.addEvent('KeyDown', function(event) {
			event = new Event(event);
			switch (event.code) {
				case Event.keys.delete: // delete
					playlist.removeSong(sel);
					setSelection([]);
					last = 0;
					clicked = 0;
					return;
				case Event.keys.up: // up
					clicked--;
					break;
				case Event.keys.down: // down
					clicked++;
					break;
				default:
					return;
			}
			last = clicked = clicked.limit(0, playlist.size - 1);
			setSelection([clicked]);
			
			var offset = (clicked + 1) * height;
			if (offset < frame.vScroll.value + height) {
				frame.vScroll.value -= height;
			}
			else if (offset > frame.vScroll.value + frame.vScroll.pageSize) {
				frame.vScroll.value += height;
			}
		});
		
		var createSongInfo = function () {
			// add songinfos and hide them.
			var songInfo = new SongInfo(playlist, frame, {
				selectionStyle: this.options.selectionStyle,
				currentStyle: this.options.currentStyle,
				defaultStyle: this.options.defaultStyle
			});
			songInfo.addEvent('Click', function (event) {
				if (event.ctrl) { 
					if (sel.contains(this.index)) {
						removeSelection(this.index);
					} else {
						addSelection(this.index);
					}
				} else if (event.shift) {
					var min = Math.min(last, this.index);
					var max = Math.max(last, this.index);
					var selection = [];
					for (var i = min; i <= max; i++) {
						selection.push(i);
					}
					setSelection(selection);
				} else {
					setSelection([this.index]);
				}
				
				clicked = this.index;
				if (!event.shift) {
					last = this.index;
				}	
			}.bindWithEvent(songInfo));
			songInfo.visible = false;
			return songInfo;
		}.bind(this);
		
		Math.ceil(frame.height / height).times(function () {
			songInfos.push(createSongInfo());
		});

		var updateFrameContent = function (width) {
			var startIndex = Math.floor(frame.vScroll.value / height);
			songInfos.length.times(function (i) {
				var index = startIndex + i;
				if (index < playlist.size) {
					songInfos[i].index = index;
					songInfos[i].width = width;
					songInfos[i].visible = true;
					songInfos[i].selected = sel.contains(index);
					songInfos[i].current = index == playlist.currentIndex;
				} else {
					songInfos[i].visible = false;
				}
			});
		}
		
		playlist.addEvent('onAddSong', function(){
			setScrollMax();
			updateFrameContent(frame.width);
		})
		.addEvent('onRemoveSong', function (songsRemoved) {
			setScrollMax();
			
			updateFrameContent(frame.width);
		}).addEvent('onPlaylistEmpty', function () {
			setSelection([]);
		})
		.addEvent('onPlaylistSorted', function() {
			songInfos.each(function (item) {
				item.index = item.index; // redisplay the text
			});
		}).addEvent('onCurrentSongChanged', function (cur, oldCur) {
			songInfos.each(function (item) {
				item.current = item.index == cur;
			});
		});
		
		frame.addEvent('HeightChanged', function (newval) {
			scroll.pageSize = newval;
			setScrollMax();
			
			var difference = newval - frame.height;
			if (difference > 0) {
				const extra = Math.ceil(difference / height);
				extra.times(function () {
					songInfos.push(createSongInfo());
				})
			}
			
			updateFrameContent(frame.width);
		}).vScroll.addEvent('ValueChanged', function (event) {
			updateFrameContent(frame.width);
		});
		
		this.addEvent('onSelectionChanged', function (selection) {
			songInfos.each(function(item){
				item.selected = selection.contains(item.index);
			});
		});
		
		var addSelection =  function (index) {
			index = index.limit(0, playlist.size - 1);
			
			if (playlist.size > 0 && !sel.contains(index)) {
				sel.push(index);
				this.fireEvent('onSelectionChanged', [sel], 0);
			}
		}.bind(this);
		
		var removeSelection =  function (index) {
			index = index.limit(0, playlist.size - 1);
			
			if (sel.contains(index)) {
				sel.remove(index);
				this.fireEvent('onSelectionChanged', [sel], 0);
			}
		}.bind(this);
		
		var setSelection =  function(indices) {
			if (playlist.size > 0) {
				sel = indices.map(function(item, index) {
					return item.limit(0, playlist.size - 1);
				}).unique();
			} else {
				sel = [];
			}
			this.fireEvent('onSelectionChanged', [sel], 0);
		}.bind(this);
	}
});
PlaylistUI.implement(new Options, new Events);